﻿using System;
using System.Collections.Generic;
using System.Linq;
using BurzaConsole.Entities;

namespace BurzaConsole
{
    public class KurzovyListokGenerator
    {
        public IEnumerable<Ticker> Tickers { get; set; }

        public int StartTime { get; set; }
        public int EndTime { get; set; }
        public int IntervalSize { get; set; }

        public int Ema1Width { get; set; }
        public int Ema2Width { get; set; }

        public KurzovyListok GenerateKurzovyListok()
        {
            var normalisedData = GetNormalisedData().ToList();
            var ema1Data = CalculateEma(normalisedData, Ema1Width).ToList();
            var ema2Data = CalculateEma(normalisedData, Ema2Width).ToList();

            var vyvojKurzu = new Dictionary<int, DataSnapshot>();

            for (var i = 0; i < normalisedData.Count; i++)
            {
                vyvojKurzu.Add(normalisedData[i].TimeStamp, new DataSnapshot
                                                            {
                                                                KurzAvg = normalisedData[i].KurzAvg,
                                                                KurzMostRecent = normalisedData[i].KurzMostRecent,
                                                                Ema1Value = ema1Data[i].KurzAvg,
                                                                Ema2Value = ema2Data[i].KurzAvg
                                                            });
            }

            return new KurzovyListok(vyvojKurzu, StartTime, EndTime, IntervalSize, Ema1Width, Ema2Width);
        }

        private IEnumerable<TimePoint> GetNormalisedData()
        {
            var timeIntervals = GetTimeIntervals().ToList();

            var normalisedTickers =
                Tickers.GroupBy(ticker =>
                    {
                        var multiplier = (int)Math.Ceiling((double)(ticker.TimeStamp - StartTime) / IntervalSize);
                        return EndTime - (timeIntervals.Count - multiplier) * IntervalSize;
                    },
                    ticker => ticker,
                    (key, group) =>
                    {
                        var groupTickers = @group as Ticker[] ?? @group.ToArray();
                        return new TimePoint
                                   {
                                       KurzAvg = groupTickers.Average(ticker => ticker.Kurz),
                                       KurzMostRecent = groupTickers.Last().Kurz,
                                       TimeStamp = key
                                   };
                    }).ToList();

            var returnValue = new List<TimePoint>();

            for (int i = 0, iterator = 0; i < timeIntervals.Count; i++)
            {
                if (iterator < normalisedTickers.Count &&
                    timeIntervals[i] == normalisedTickers[iterator].TimeStamp)
                {
                    returnValue.Add(normalisedTickers[iterator]);
                    iterator++;
                }
                else
                    returnValue.Add(new TimePoint
                                        {
                                            KurzAvg = returnValue.Count == 0 ? 0 : returnValue.Last().KurzMostRecent,
                                            KurzMostRecent = returnValue.Count == 0 ? 0 : returnValue.Last().KurzMostRecent,
                                            TimeStamp = timeIntervals[i]
                                        });
            }

            return returnValue;
        }

        private static IEnumerable<TimePoint> CalculateEma(IReadOnlyList<TimePoint> normalisedData, int emaWidth)
        {
            var resultArray = new List<TimePoint>();

            for (var i = 0; i < normalisedData.Count; i++)
            {
                var kurz = 0.0;

                for (var j = 0; j < emaWidth; j++)
                {
                    var index = i - j;

                    if (index < 0) break;

                    kurz += normalisedData[index].KurzAvg;
                }

                resultArray.Add(new TimePoint
                {
                    KurzAvg = kurz / emaWidth,
                    TimeStamp = normalisedData[i].TimeStamp
                });
            }

            return resultArray;
        }

        private IEnumerable<int> GetTimeIntervals()
        {
            var list = new List<int>();
            var timeBlockCount = (int)Math.Ceiling((double)(EndTime - StartTime) / IntervalSize);

            for (var i = 0; i < timeBlockCount; i++)
            {
                list.Add(EndTime - i * IntervalSize);
            }

            list.Reverse();

            return list;
        }
    }
}